﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using PConexion;
using System.Configuration;
using PProtocolo;
using System.Collections;
using System.Threading;
using System.Messaging;
using Eventos;

namespace Cliente
{
    class Client
    {
        private static int BUFFER_SIZE = 1024;
        private static string ipservidor;
        private static string puerto;
        private static Semaphore semaforo = new Semaphore(1, 1);

        static void Main(string[] args)
        {
            //Obtengo ip y puerto del servidor
            ipservidor = ConfigurationManager.AppSettings["IP_SERVIDOR"];
            puerto = ConfigurationManager.AppSettings["PUERTO"];
            string carpetaDeTrabajo = ConfigurationManager.AppSettings["CARPETA_DE_TRABAJO"];
            Console.Title = "CLIENTE";

            Conexion conexion = new ConexionCliente();

            string IPServidorNotificacion = ConfigurationManager.AppSettings["SERVIDOR_ACTIVIDAD"];
            string QueueMensaje = ConfigurationManager.AppSettings["RUTA_COLA_CLIENTES"];
            conexion.IPServidorNotificacion = IPServidorNotificacion;
            conexion.NombreNotificacion = QueueMensaje;

            //Parseamos el puerto
            int puertoServidor = 0;
            int.TryParse(puerto, out puertoServidor);

            try
            {
                bool exit = false;
                String comandoUsuario;
                Console.WriteLine("::: Bienvenido a la aplicación cliente :::\n");

                ProtocoloCliente cliente = new ProtocoloCliente();
                cliente.EstablecerConexion(conexion);

                while(!exit){
                    Console.Write("COMANDO: ");
                    comandoUsuario= Console.ReadLine();
                    int comando=InterpretadorComando(comandoUsuario);
                    switch(comando)
                    {
                        case 1:
                            //CONECTAR
                            Console.Write("Ingrese la IP del servidor o escriba default: ");
                            ipservidor = Console.ReadLine();

                            if (ipservidor != "default")
                            {
                                Console.Write("Ingrese el Puerto: ");
                                puerto = Console.ReadLine();
                            }
                            else
                                ipservidor = ConfigurationManager.AppSettings["IP_SERVIDOR"];

                            if (ValidarIpPuerto(ipservidor, puerto))
                            {
                                int.TryParse(puerto, out puertoServidor);
                                if (conexion.AbrirConexion(ipservidor, puertoServidor))
                                {
                                    Console.WriteLine("\nEXITO: Se pudo establecer la conexión.\n");

                                    //Notifico que me conecté al servidor
                                    string idSesion = cliente.ObtenerIdSesion();
                                    string IpServidorCola = ConfigurationManager.AppSettings["IP_SERVIDOR_COLA"];
                                    Manejador m = new Manejador();
                                    IPEndPoint ipCliente = cliente.conexion.SocketConexion.LocalEndPoint as IPEndPoint;
                                    m.EnviarEventoMOM(IpServidorCola,QueueMensaje,new Evento(idSesion,ipCliente.Address.ToString(),DateTime.Now,"Cliente pudo conectarse",Evento.Comandos.CONEXION)); 
                                }
                                else
                                {
                                    Console.WriteLine("CLIENTE: No se pudo establecer la conexión.\n");
                                }
                            }
                            else
                            {
                                Console.WriteLine("ERROR: Los datos ingresados no son correctos.\n");
                            }
                            break;
                        case 2:
                            //LISTAR ARCHIVOS
                            if (conexion.ConexionActiva())
                            {
                                cliente.ListarArchivosREQ();//Envio la solicitud de listar los archivos
                                ArrayList archivos;
                                archivos = cliente.ListarArchivosRES();
                                if (archivos.Count > 0)
                                {
                                    Console.WriteLine("\nLos archivos disponibles en el servidor son:");
                                    foreach (var archivo in archivos)
                                    {
                                        Console.WriteLine("-> " + archivo.ToString());
                                    }
                                }
                                else
                                {
                                    Console.WriteLine("No hay archivos en el servidor.\n");
                                }                            
                            }
                            else
                                Console.WriteLine("La conexion no se encuentra disponible.\n");                            
                            break;
                        case 3:
                            //DESCONECTAR
                            //Notifico que me desconecté al servidor
                            cliente.Desconectar();

                            string idSesionDes = cliente.ObtenerIdSesion();
                            string IpServidorColaDes = ConfigurationManager.AppSettings["IP_SERVIDOR_COLA"];
                            Manejador ma = new Manejador();
                            IPEndPoint ipClienteDes = cliente.conexion.SocketConexion.LocalEndPoint as IPEndPoint;
                            ma.EnviarEventoMOM(IpServidorColaDes, QueueMensaje, new Evento(idSesionDes, ipClienteDes.Address.ToString(), DateTime.Now, "Cliente se desconecto", Evento.Comandos.DESCONEXION)); 

                            if (conexion.CerrarConexion())
                            {
                                Console.WriteLine("EXITO: Se cerró la conexión exitosamente.\n");
                            }
                            else
                            {
                                Console.WriteLine("ERROR: No se pudo cerrar la conexión.\n");
                            }
                            break;
                        case 4:
                            //CARGAR UN ARCHIVO AL SERVIDOR
                            if (conexion.ConexionActiva())
                            {
                                Console.WriteLine("\nIngrese la ruta del archivo a enviar:");
                                string rutaArchivo = Console.ReadLine();
                                Console.WriteLine("\nIngrese el nombre del nuevo archivo:");
                                string nombreNuevoArchivo = Console.ReadLine();
                                //semaforo.WaitOne();
                                long tamanioArchivo = ValidarArchivo(rutaArchivo);
                                if (tamanioArchivo != -1)
                                {
                                    cliente.EnviarArchivoREQ(nombreNuevoArchivo, tamanioArchivo);
                                    cliente.EnviarArchivoRES(rutaArchivo, tamanioArchivo);

                                    string idSesionCar = cliente.ObtenerIdSesion();
                                    string IpServidorColaCar = ConfigurationManager.AppSettings["IP_SERVIDOR_COLA"];
                                    Manejador mCar = new Manejador();
                                    IPEndPoint ipClienteCar = cliente.conexion.SocketConexion.LocalEndPoint as IPEndPoint;
                                    mCar.EnviarEventoMOM(IpServidorColaCar, QueueMensaje, new Evento(idSesionCar, ipClienteCar.Address.ToString(), DateTime.Now, "Cliente carga archivo "+nombreNuevoArchivo, Evento.Comandos.CARGA)); 
                                }
                                else
                                {
                                    Console.WriteLine("ERROR: La ruta indicada no es correcta.\n");
                                }
                                //semaforo.Release();
                            }
                            else
                                Console.WriteLine("ERROR: La conexion no se encuentra disponible.\n");
                            break;
                        case 5:
                            //DESCARGAR UN ARCHIVO
                            if (conexion.ConexionActiva())
                            {
                                Console.WriteLine("\nIngrese el nombre del archivo a descargar.:");
                                                                
                                string nombreArchivoADescargar = Console.ReadLine();

                                cliente.DescargarArchivoREQ(nombreArchivoADescargar);

                                long tamanio = cliente.DescargarArchivoRES();

                                if (tamanio != -1)
                                {
                                    Console.WriteLine("Recepcionando archivo...\n");
                                    cliente.DescargarArchivoRESContenido(carpetaDeTrabajo + "\\" + nombreArchivoADescargar, tamanio);

                                    string idSesionDesc = cliente.ObtenerIdSesion();
                                    string IpServidorColaDesc = ConfigurationManager.AppSettings["IP_SERVIDOR_COLA"];
                                    Manejador mDes = new Manejador();
                                    IPEndPoint ipClienteDesc = cliente.conexion.SocketConexion.LocalEndPoint as IPEndPoint;
                                    mDes.EnviarEventoMOM(IpServidorColaDesc, QueueMensaje, new Evento(idSesionDesc, ipClienteDesc.Address.ToString(), DateTime.Now, "Cliente descarga archivo "+nombreArchivoADescargar, Evento.Comandos.DESCARGA)); 

                                }
                                else
                                {
                                    Console.WriteLine("ERROR: El archivo seleccionado no existe\n");
                                }
                                
                            }
                            break;
                        case 6:
                            //SINCRONIZAR
                            Console.Write("Ingrese la IP del servidor con el cual sincronizar: ");
                            ipservidor = Console.ReadLine();

                          
                            Console.Write("Ingrese el Puerto: ");
                            puerto = Console.ReadLine();

                            if (ValidarIpPuerto(ipservidor, puerto))
                            {
                                int.TryParse(puerto, out puertoServidor);
                                    
                                cliente.SincronizarREQ(ipservidor, puertoServidor);//Envio solicitud de sincronizacion

                                 int respuestaServidor=cliente.SincronizarRES();

                                 if (respuestaServidor==72)
                                 {
                                    Console.WriteLine("\nEl servidor pudo sincronizarse.");
                                 }
                                 else
                                 {
                                     Console.WriteLine("Cliente: El servidor no puede sincronizar.");
                                 }
                                 
                            }
                            else
                            {
                                Console.WriteLine("ERROR: Los datos ingresados no son correctos.\n");
                            }
                            break;
                        case 10:
                            //AYUDA
                            ListarComandos();
                            break;
                        case 99:
                            exit = true;
                            if (conexion.ConexionActiva())
                            {
                                Console.WriteLine("ERROR: No es posible salir de la aplicación: debe cerrar la conexión primero.");
                            }
                            else
                            {
                                Console.WriteLine("Cerrando aplicacion...");
                            }
                            break;
                        default:
                            Console.WriteLine("CLIENTE: No se pudo interpretar el comando ingresado.\nIngrese la palabra ayuda para ver los comandos disponibles");
                            break;
                    }
                    Console.WriteLine("\n");
                
                }
            }
            catch (SocketException ex)
            {
                conexion.CerrarConexion();                
                Console.ReadLine();
            }
        }

        private static int InterpretadorComando(String textoUsuario)
        {
            int retorno = 0;
            switch (textoUsuario)
            {
                case "conectar":
                    retorno = 1;
                    break;
                case "listar":
                    retorno = 2;
                    break;
                case "desconectar":
                    retorno = 3;
                    break;
                case "salir":
                    retorno = 99;
                    break;
                case "cargar":
                    retorno = 4;
                    break;
                case "descargar":
                    retorno = 5;
                    break;
                case "sincronizar":
                    retorno = 6;
                    break;
                case "ayuda":
                    retorno = 10;
                    break;
                default:
                    //No se interpreto el comando
                    retorno = -1;
                    break;
            }
            return retorno;
        }

        private static long ValidarArchivo(string rutaArchivo)
        { 
            //Valido si existe el archivo
            if (System.IO.File.Exists(rutaArchivo))
            {
                FileInfo f = new FileInfo(rutaArchivo);
                return f.Length;
            }
            return -1;
        }

        private static void ListarComandos()
        {
            Console.WriteLine("\nLos comandos disponibles son:\n");
            Console.WriteLine("conectar : permite la conexión con el servidor, ingresando la IP y el puerto o usando la conexion por defecto.");
            Console.WriteLine("listar : se listan los archivos disponibles en el servidor");
            Console.WriteLine("cargar : permite subir un archivo al servidor");
            Console.WriteLine("descargar : permite descargar un archivo del servidor");
            Console.WriteLine("sincronizar : sincroniza la información entre dos servidores");
            Console.WriteLine("desconectar : finaliza la conexion con el servidor");
            Console.WriteLine("salir : cierra la aplicación");
        }

        private static bool ValidarIpPuerto(string IpServidor, string PuertoServidor)
        { 
            IPAddress address;
            int port;
            if (IPAddress.TryParse(IpServidor,out address))
            {
                if (int.TryParse(PuertoServidor, out port))
                {
                    return true;
                }
            }
            return false;
        }

        private static bool ValidarIpsDeSincronizacion(string IpServidor1, string IpServidor2)
        { 
            IPAddress address;
            if (IPAddress.TryParse(IpServidor1, out address))
            {
                if (IPAddress.TryParse(IpServidor2, out address))
                {
                    return true;
                }
            }
            return false;
        }

    }

}
